//
//  tdl/tdlexcept.h
//
//  Created by tommy on 12/9/17.
//

#ifndef tdl_tdlexcept_h
#define tdl_tdlexcept_h

#include <assert.h>
#include <string.h>
#include <sstream>
#include <stdexcept>
#include "tdltypes.h"
using namespace tdl;

namespace tdl {
///////////////
class tdl_exception : public std::exception
{
public:
	explicit tdl_exception(const std::string& serr) : m_errstr(serr), m_errcode(-1) {}
	explicit tdl_exception(const char* serr) : m_errstr(serr), m_errcode(-1) {}
	explicit tdl_exception(const std::string& serr, int err_code, bool is_sys_errcode = false) : m_errstr(serr), m_errcode(err_code)
	{
		ostringstream oss;
		oss << " [errno=" << m_errcode << "] ";
		if(is_sys_errcode)
			oss << ::strerror(m_errcode);
		m_errstr += oss.str();
	}

	~tdl_exception() throw() {}

	virtual const char* what() const throw() { return m_errstr.c_str(); }
	const std::string what_hell() const throw() { return m_errstr; }
	
	int get_errcode() { return m_errcode; }

protected:
	std::string		m_errstr;
	int				m_errcode;
};

#define TDL_DECLARE_EXCEPTION(EX_NAME)	\
	struct EX_NAME : public tdl_exception\
	{\
		EX_NAME(const std::string& serr) : tdl_exception(serr) {}\
		EX_NAME(const std::string& serr, int err_code, bool is_sys_errcode = false) : tdl_exception(serr, err_code, is_sys_errcode) {}\
	}

#define THROW(EX, ERRMSG) \
	{\
		std::ostringstream __oss__;\
		__oss__ << ERRMSG;\
		throw EX(__oss__.str());\
	}
#define THROW2(EX, ERRMSG) \
	{\
		std::ostringstream __oss__;\
		__oss__ << ERRMSG;\
		throw EX(__oss__.str(), errno, true);\
	}
	
////////////////
#if defined(__GNUC__)
#	define TDL_CURRENT_FUNCTION	__PRETTY_FUNCTION__
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)
#	define TDL_CURRENT_FUNCTION	__func__
#else
#	define TDL_CURRENT_FUNCTION	"(unknown)"
#endif

inline std::string __trim_FILE_prefix_path(const std::string& s, const std::string& prefix)
{
	int pos = s.find(prefix);
	return (pos >= 0) ? s.substr(pos + 1) : s;
}
#define TRACE_INFO()	"[" << __trim_FILE_prefix_path(__FILE__, "/tsdl/") << ":" << __LINE__ << "\t" << TDL_CURRENT_FUNCTION << "] "

#define tdl_assert(expr) tdl_assert_impl(expr, #expr, __FILE__, __LINE__, __PRETTY_FUNCTION__)
inline void tdl_assert_impl(bool succ, const char *__assertion, const char *__file, unsigned int __line, const char *__function)
{
	if(! succ)
		__assert_fail(__assertion, __file, __line, __function);
}

/////////////////
TDL_DECLARE_EXCEPTION(alloc_ex);
TDL_DECLARE_EXCEPTION(shm_ex);
TDL_DECLARE_EXCEPTION(bitmap_ex);
TDL_DECLARE_EXCEPTION(mohash_ex);
TDL_DECLARE_EXCEPTION(socket_ex);
TDL_DECLARE_EXCEPTION(epoll_ex);
TDL_DECLARE_EXCEPTION(soload_ex);
TDL_DECLARE_EXCEPTION(file_ex);
TDL_DECLARE_EXCEPTION(log_ex);
TDL_DECLARE_EXCEPTION(conf_ex);
TDL_DECLARE_EXCEPTION(mysqlc_error);

/////////////
}	// namespace tdl

#endif

